package search;

import helpers.DateHelper;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.logging.Logger;

import models.UserModel;

import org.apache.jasper.tagplugins.jstl.core.Remove;

import com.google.appengine.api.search.Document;
import com.google.appengine.api.search.Field;
import com.google.appengine.api.search.Index;
import com.google.appengine.api.search.IndexSpec;
import com.google.appengine.api.search.ListRequest;
import com.google.appengine.api.search.ListResponse;
import com.google.appengine.api.search.Query;
import com.google.appengine.api.search.QueryOptions;
import com.google.appengine.api.search.Results;
import com.google.appengine.api.search.ScoredDocument;
import com.google.appengine.api.search.SearchServiceFactory;

import entities.User;

public class UserSearchEngine {
	private static Index INDEX = SearchServiceFactory.getSearchService().getIndex(IndexSpec.newBuilder().setName("gea001"));
	
	private static final Logger log = Logger.getLogger(UserSearchEngine.class.getName());
	
	public static void index(User user) {
		try {
			// TODO: handle exception
			Document.Builder docBuilder = Document.newBuilder()
											.addField(Field.newBuilder().setName("username").setText(user.getUsername()))
											.addField(Field.newBuilder().setName("name").setText(user.getName()))
											.addField(Field.newBuilder().setName("mobile").setText(user.getMobile()));
			
			Document doc = docBuilder.build();
			INDEX.add(doc);
			System.out.println("User " + user.getUsername() + " indexed");
			log.info("User " + user.getUsername() + " indexed");
		} catch (Exception e) {
			log.warning("Error when try to index user " + user.getUsername() + " !");
			log.warning(e.toString());
		}
	}
	
	public static void update(User user) {
		remove(user.getUsername());
		index(user);
	}
	
	public static void remove(String username) {
		try {
			Query query = Query.newBuilder().setOptions(QueryOptions.newBuilder().build()).build("username = \"" + username  + "\"");
			Results<ScoredDocument> docResult = INDEX.search(query);
			if (docResult.getNumberFound() > 0) {
				for (ScoredDocument scoredDocument : docResult) {
					INDEX.remove(scoredDocument.getId());
				}
				System.out.println("Remove order index success !");
			}
			
		} catch (Exception e) {
			System.out.println("Error when try to remove user index !");
			log.warning("Error when try to remove user index !");
			log.warning(e.toString());
		}
	}
	
	public static void removeAllDocument() {
		int count = 0;
		List<String> docIds = new ArrayList<String>();
		//Return a set off document Ids
		ListRequest request = ListRequest.newBuilder().setReturningIdsOnly(true).build();
		ListResponse<Document> response = INDEX.listDocuments(request);
		if (response.getResults().isEmpty()) {
			return;
		}
		
		for (Document doc:response) {
			docIds.add(doc.getId());
			count ++;
		}
		INDEX.remove(docIds);
		System.out.println("Remove " + count + " document from Index success !");
	}
	
	public static void reIndex() {
		int count = 1;
		UserModel userModel = UserModel.getInstance();
		while (true) {
			List<User> users = userModel.list(count, 499);
			for (User user : users) {
				index(user);
				count ++;
			}
			
			if (users.size() < 488) {
				break;
			}
		}
		System.out.println("Index " + count + " document from User success !");
	}
	
	public static Document.Builder objectToSearchDocument(Document.Builder docBuilder, Object obj) {
		for (java.lang.reflect.Field f : obj.getClass().getDeclaredFields()) {
			f.setAccessible(true);
			if (f.getType().getSimpleName().equals("String")) {
				try {
					docBuilder.addField(Field.newBuilder().setName(f.getName())
							.setText((String) f.get(obj)));
				} catch (Exception e) {

				}
			} else if (f.getType().getSimpleName().equals("int")) {
				try {
					docBuilder.addField(Field.newBuilder().setName(f.getName())
							.setNumber(f.getInt(obj)));
				} catch (Exception e) {

				}
			} else if (f.getType().getSimpleName().equals("Date")) {
				try {
					docBuilder.addField(Field
							.newBuilder()
							.setName(f.getName())
							.setDate(
									DateHelper.removeTimeFromDate((Date) f
											.get(obj))));
				} catch (Exception e) {

				}
			} else if (f.getType().getSimpleName().equals("boolean")) {
				try {
					docBuilder.addField(Field.newBuilder().setName(f.getName())
							.setText((String) f.get(obj)));
				} catch (Exception e) {

				}
			}
		}
		return docBuilder;
	}
	
	public static User documentToObjectByReflection(ScoredDocument doc) {
		User obj = new User();
		for (java.lang.reflect.Field f : obj.getClass().getDeclaredFields()) {
			f.setAccessible(true);
			if (f.getType().getSimpleName().equals("String")) {
				try {
					StringBuilder tmpString = new StringBuilder();
					tmpString.append(doc.getOnlyField(f.getName()).getText());
					f.set(obj, tmpString.toString());
				} catch (Exception e) {
					// TODO: handle exception
				}
			} else if (f.getType().getSimpleName().equals("int")) {
				try {
					f.set(obj, doc.getOnlyField(f.getName()).getNumber().intValue());
				} catch (Exception e) {
					// TODO: handle exception
				}
			} else if (f.getType().getSimpleName().equals("Date")) {
				try {
					f.set(obj, doc.getOnlyField(f.getName()).getDate());
				} catch (Exception e) {
					// TODO: handle exception
				}
			} else if (f.getType().getSimpleName().equals("boolean")) {
				try {
					f.set(obj, doc.getOnlyField(f.getName()).getText().equals("true") ? true : false);
				} catch (Exception e) {
					// TODO: handle exception
				}
			}
		}
		return obj;
	}
}
